package com.flca.frw.components
{
	import flash.events.EventDispatcher;
	import flash.display.DisplayObject;
	import flash.events.MouseEvent;
	import mx.controls.Alert;
	import mx.core.Container;
	import mx.core.UIComponent;
	import mx.events.CloseEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.events.FaultEvent;
	
	import com.flca.frw.events.EventSaveComplete;
	import com.flca.frw.events.EventReadyForClose;
	import com.flca.frw.events.EventStatusUpdate;
	import com.flca.frw.util.ValidationUtil;
	import mx.containers.Panel;
	import com.flca.frw.config.TranslateUtilFactory;
	import com.flca.frw.interfaces.ICrudCtlr;
	import mx.collections.ArrayCollection;
	import mx.containers.VBox;
	
	[Event(name=EventSaveComplete.SAVE_COMPLETE,    type="framework.events.EventSaveComplete")]
	[Event(name=EventReadyForClose.READY_FOR_CLOSE, type="framework.events.EventReadyForClose")]
	[Event(name=EventOpenNewView.OPEN_NEW_VIEW,     type="framework.events.EventOpenNewView")]
	[Event(name=EventStatusUpdate.STATUS_UPDATE,    type="framework.events.EventStatusUpdate")]

	public class BaseCrudCtlr extends VBox implements ICrudCtlr
	{
		public const UPD_VIEW:Boolean = true;
		public const UPD_MODEL:Boolean = false;

		protected var readOnly:Boolean = true;
		
		protected var initialHash:int;
		protected var closeAfterSave:Boolean = false;
		
		protected var validationOk:Boolean = true;
		
		public var persistMode:Boolean = true;

		public function BaseCrudCtlr()
		{
			// normally, there is nothing to do here
			// add code in subclasses when necessary
		}
		
		public function getDisplayName():String
		{
			// OVERRIDE this method
			
			return "";
		}

		private var modelGetter : Function;
		
		[Bindable]
		private var resultColl : ArrayCollection;
		
		public function setModelGetter(value:Function) : void {
			modelGetter = value;			
		}

		public function getCurrentMdl():Object {
			if (modelGetter != null) {
				return modelGetter();
			} else {
				return null;
			}
		}
			
		protected function get resultCollection() : ArrayCollection {
			if (resultColl == null) {
				resultColl = new ArrayCollection();
			}
			return resultColl;
		}
		
		protected function set resultCollection(values : ArrayCollection) : void {
			resultColl = values;
		}
		
		public function getInitialHash():int
		{
			return initialHash;
		}
		
		public function init(aInitialHash:int = 0):void
		{
			// DO NOT OVERRIDE this method ... use initComplete()
			
			initialHash = hashMdl();
			updateViewFromModel();
			initComplete();
			manageGuiState();
		}
		
		protected function initComplete():void
		{
			// OVERRIDE this method if desired
			
			return;
		}

		protected function hashMdl():int
		{
			// OVERRIDE this method
			
			// This method is typically used to calculate
			// the hashcode of the current model in order
			// to perform dirty checks upon close
			return 0;
		}
		
		public function updateViewFromModel():void
		{
			// OVERRIDE this method
			
			// This method is typically used to copy the
			// model values to the UI controls
			
			return;
		}
		
		public function updateModelFromView():void
		{
			// OVERRIDE this method
			
			// This method is typically used to copy the
			// UI values to the model
			
			return;
		}
		
		public function close():void
		{
			btnCloseClicked(null);
		}
			
		protected function btnCloseClicked(aEvent:MouseEvent):void
		{
			var hash:int;
			
			if (hasModel) {
				updateModelFromView();
				hash = hashMdl();
			}
			
			if (hasModel && !readOnly && hash != initialHash)
			{
				// there are unsaved changes!
				Alert.show("There are unsaved changes. Would you like to save now?", "Warning!", Alert.YES | Alert.NO | Alert.CANCEL, null, handleAlertOutcome, null, Alert.YES);
			}
			else
			{
				dispatchEvent(new EventReadyForClose(getCurrentMdl()));
			}
		}
		
		private function handleAlertOutcome(aEvent:CloseEvent):void
		{
			if (aEvent.detail == Alert.YES)	{
				// listen for save-complete event, 
				// since view needs to be closed
				addEventListener(EventSaveComplete.SAVE_COMPLETE, handleSaveCompleteOnClose);
 			} else if (aEvent.detail == Alert.CANCEL){
				// do nothing. Stay in the window
			} else	{
				dispatchEvent(new EventReadyForClose(getCurrentMdl()));
			}
		}
		
		private function handleSaveCompleteOnClose(aEvent:EventSaveComplete):void
		{
			dispatchEvent(new EventReadyForClose());
		}
		
		public function isSaveNeeded() : Boolean
		{
			var hash:int;
			var msg:String;
			
			hash = hashMdl();
			
			//@TODO hash wordt nog niet gebruikt
			if (true || !hasModel || hash != initialHash) {
				return true;
			} else {
				sendStatusUpdate("nothing to save");
				Alert.show("No changes made. There is nothing to save.", "Application Feedback");
				return false;				
			}
		}
		
		public function validate() : Boolean
		{
			var msg:String = validateView();
			if (msg == null) {
				return true;
			} else {
				sendStatusUpdate("validation failed");
				Alert.show(msg, "Application Feedback");
				return false;
			}
		}

		protected function get hasModel() : Boolean {
			return getCurrentMdl() != null;
		}

		protected function validateView():String
		{
			// OVERRIDE this method only if additional validation
			// is required. Don't forget to call this method from
			// the overriden method!!!
			// If all validations are passed 'null' should be returned,
			// otherwise a compelling message about the validation error
			
			var result:String;
			
			if (ValidationUtil.validateAll(getValidators())) {
				result = null;
			} else {
				result = "Not all validations are passed.";
			}
			
			return result;
		}
			
		protected function getValidators():Array
		{
			// OVERRIDE this method
			
			// Normally this method returns all
			// relevant validators of the view
			
			return new Array();
		}
		

		protected function handleWsResult(aResult:ResultEvent):void
		{
			sendStatusUpdate("save ok");
			
			var resultMdl:Object;
			
			if (hasModel) {
				resultMdl = aResult.result;
//				setCurrentMdl(resultMdl);
				init();
			}
			else
			{
				resultMdl = null;
			}
			
			dispatchEvent(new EventSaveComplete(resultMdl));
			
			if (closeAfterSave)
			{
				dispatchEvent(new EventReadyForClose(getCurrentMdl()));
			}
		}
		
		protected function handleWsFault(aFault:FaultEvent):void
		{
			sendStatusUpdate("save failed");
			
			// remove listener for save-complete event, 
			// if it was possibly registered before
			removeEventListener(EventSaveComplete.SAVE_COMPLETE, handleSaveCompleteOnClose);
		}
		
		protected function manageGuiState():void
		{
			// OVERRIDE this method
			
			// This method is typically used to enable/
			// disable/visualize GUI controls dependent
			// on the model context
			
			return;
		}
		
		protected function sendStatusUpdate(aMessage:String):void
		{
			dispatchEvent(new EventStatusUpdate(aMessage));
		}
		
		public function getCallbackReadyForClose():Function
		{
			// OVERRIDE this method
			//
			// only if this controller is interested in the close
			// event of a possible child controller
			
			return null;
		}
		
		public function getCallbackSaveComplete():Function
		{
			// OVERRIDE this method
			//
			// only if this controller is interested in the save complete
			// event of a possible child controller
			
			return null;
		}
		
		public function get createMode() : Boolean {
			// OVERRIDE THIS METHOD
			return true;	
		}
		
		protected function trl(value: String) : String
		{
			return TranslateUtilFactory.getInstance().translate(value);
		}
		
	}
}